﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GitDiff
{
    class GitDiffOperation
    {
        #region 预定义
        private const string GIT_COMMAND_STRING_HELP = "git help";
        private const string GIT_COMMAND_STRING_GET_STATUS = "git status -s";
        private const string GIT_COMMAND_STRING_GET_SHOW_HEAD = "git show head:";
        private const string GIT_COMMAND_STRING_CHECKOUT_HEAD = "git checkout -- ";
        private const string GIT_COMMAND_STRING_CHECKOUT_STASH = "git checkout -- .";
        private const string GIT_COMMAND_STRING_RESET_FILE_FROM_STASH = "get reset "; 
        private const string GIT_FILE_STATUS_HEAD_ADD = "??";
        private const string GIT_FILE_STATUS_HEAD_MODIFY = "M ";
        private const string GIT_FILE_STATUS_HEAD_DELETE = "D ";
        private const string TMP_FOLDER_NAME = "_tmp_gitdiff_files";
        private enum CommandType
        {
            NONE = 0,
            CMD_GET_STATUS,                         // 获取文件状态
            CMD_GET_SHOW_HEAD,                      // 读取最近历史版本
            CMD_GET_SHOW_HEAD_TO_DIRECTION_FILE,    //
            CMD_GET_CHECKOUT_HEAD,                  //
            CMD_GET_CHECKOUT_STASH,                 //
            CMD_GET_RESET_FILE_FROM_STASH           //
        };
        private GitCommand oTerminal = new GitCommand();
        #endregion

        /// <summary>
        /// 获取异常状态文件一览
        /// </summary>
        /// <returns></returns>
        public bool getModeifyFileList(string strSourcePath, out string strMsg,out string strFileList)
        {
            bool bReturn = false;
            strMsg = string.Empty;
            strFileList = string.Empty;
            StringBuilder strAddFile = new StringBuilder();
            StringBuilder strModifyFile = new StringBuilder();

            try
            {
                // 1. [命令作成]文件状态
                string sCommandParam = makeCommand(strSourcePath, (int)CommandType.CMD_GET_STATUS);
                // 2. [命令执行]文件状态
                string strResult = oTerminal.Exec(sCommandParam).Replace("/", "\\");
                if (!string.IsNullOrWhiteSpace(strResult))
                {
                    if (strResult.IndexOf("不是内部或外部命令，也不是可运行的程序") > 0)
                    {
                        strMsg = "当前系统未配置Git相关环境变量，请联系程序狗！";
                        return bReturn;
                    }
                    var strArryResult = strResult.Split(Environment.NewLine.ToCharArray());
                    foreach (var item in strArryResult)
                    {
                        if (string.IsNullOrWhiteSpace(item))
                            continue;

                        string tmpString = item.Trim();
                        if (tmpString.Substring(0,2) == GIT_FILE_STATUS_HEAD_ADD)              //新增文件
                            strAddFile.AppendLine(string.Format("[ADD] \\{0}", tmpString.Replace(GIT_FILE_STATUS_HEAD_ADD, "").Replace("\"", "").Trim()));
                        else if (tmpString.StartsWith(GIT_FILE_STATUS_HEAD_MODIFY))      //修改文件
                            strModifyFile.AppendLine(string.Format("[MOD] \\{0}", tmpString.Replace(GIT_FILE_STATUS_HEAD_MODIFY, "").Replace("\"", "").Trim()));
                        else if (tmpString.StartsWith(GIT_FILE_STATUS_HEAD_DELETE))       //删除文件
                            strAddFile.AppendLine(string.Format("[DEL] \\{0}", tmpString.Replace(GIT_FILE_STATUS_HEAD_DELETE, "").Replace("\"", "").Trim()));
                    }

                    // [显示用]差分文件一览
                    if (strAddFile.Length > 0 || strModifyFile.Length > 0)
                    {
                        strFileList = strModifyFile.AppendLine(strAddFile.ToString()).ToString();
                        bReturn = true;
                    }
                }
                else
                {
                    strMsg = "系统参数异常，请联系程序狗！";
                }
            }
            catch (Exception ex)
            {
                strMsg = "系统异常：" + ex.Message;
            }
            return bReturn;
        }

        /// <summary>
        /// 终端操作命令作成
        /// </summary>
        /// <param name="sSourceFilePath">源目录</param>
        /// <param name="opType">Git命令类型</param>
        /// <param name="sSourceSubFilePath"></param>
        /// <returns></returns>
        public string makeCommand(string sSourceFilePath, int opType = 0, string sSourceSubFilePath = "", string sDirectionFilePath = "")
        {
            string strCommand = string.Empty;
            string sDisk = string.Empty;

            #region 获取迁移源盘符
            if (sSourceFilePath.IndexOf('\\') > 0)
                sDisk = sSourceFilePath.Substring(0, sSourceFilePath.IndexOf('\\'));

            if (!string.IsNullOrWhiteSpace(sDisk))
            {
                strCommand = sDisk;
                strCommand += "&" + string.Format("cd {0}", sSourceFilePath);
            }
            else
            {
                return string.Empty;
            }
            #endregion

            switch (opType)
            {
                // 获取修正文件明细
                case (int)CommandType.CMD_GET_STATUS:
                    strCommand += "&" + GIT_COMMAND_STRING_GET_STATUS;
                    break;
                case (int)CommandType.CMD_GET_SHOW_HEAD:
                    strCommand += "&" + GIT_COMMAND_STRING_GET_SHOW_HEAD + sSourceSubFilePath;
                    break;
                case (int)CommandType.CMD_GET_CHECKOUT_HEAD:
                    strCommand += "&" + GIT_COMMAND_STRING_CHECKOUT_HEAD;
                    break;
                case (int)CommandType.CMD_GET_CHECKOUT_STASH:
                    strCommand += "&" + GIT_COMMAND_STRING_CHECKOUT_STASH;
                    break;
                case (int)CommandType.CMD_GET_SHOW_HEAD_TO_DIRECTION_FILE:
                    string subCmd = string.Empty;
                    subCmd = string.Format("&{0}{1} >{2}", GIT_COMMAND_STRING_GET_SHOW_HEAD, sSourceSubFilePath, sDirectionFilePath);
                    strCommand += subCmd;
                    break;
                case (int)CommandType.CMD_GET_RESET_FILE_FROM_STASH:
                    strCommand += "&" + GIT_COMMAND_STRING_RESET_FILE_FROM_STASH + sSourceSubFilePath;
                    break;
                default:
                    break;
            }
            return strCommand;
        }

        /// <summary>
        /// 修改后文件拷贝
        /// </summary>
        /// <param name="sFilePath"></param>
        /// <param name="strSourcePath"></param>
        /// <param name="sDirectionPath"></param>
        /// <returns></returns>
        public bool CopyModifyFile(List<string> sFilePath, string strSourcePath, string sDirectionPath, out string strMsg)
        {
            bool bResult = false;
            strMsg = "";

            if (sFilePath.Count == 0 || string.IsNullOrWhiteSpace(strSourcePath))
            {
                strMsg = "系统异常：无法定位待拷贝文件！";
                return false;
            }

            try
            {
                string[] strArray = sFilePath.ToArray();
                string sSourceFileFullPath = string.Empty, sTargetFileFullPath = string.Empty;

                #region 异常状态文件抽出 -> 迁移后目录
                foreach (string item in strArray)
                {
                    if (string.IsNullOrWhiteSpace(item))
                        continue;

                    if (item.StartsWith("[DEL]"))
                        continue;

                    string sSubFilePath = item.Substring(6, item.Length - 6);
                    sSourceFileFullPath = string.Format("{0}{1}", strSourcePath, sSubFilePath);
                    sTargetFileFullPath = string.Format("{0}\\0.修正后{1}", sDirectionPath, sSubFilePath);

                    if (item.StartsWith("[MOD]"))
                    {
                        #region 拷贝文件
                        string sSubDirectionPath = sTargetFileFullPath.Substring(0, sTargetFileFullPath.LastIndexOf("\\"));
                        if (!Directory.Exists(sSubDirectionPath))
                            Directory.CreateDirectory(sSubDirectionPath);

                        File.Copy(sSourceFileFullPath, sTargetFileFullPath, true);
                        #endregion
                    }
                    else if (item.StartsWith("[ADD]"))
                    {
                        List<string> lAddFile;
                        if(GetAllFolderFiles(sSourceFileFullPath,out lAddFile, out strMsg))
                        {
                            foreach (string file in lAddFile)
                            {
                                sTargetFileFullPath = string.Format("{0}\\0.修正后{1}", sDirectionPath, file.Replace(strSourcePath, ""));

                                #region 拷贝文件
                                string sSubDirectionPath = sTargetFileFullPath.Substring(0, sTargetFileFullPath.LastIndexOf("\\"));
                                if (!Directory.Exists(sSubDirectionPath))
                                    Directory.CreateDirectory(sSubDirectionPath);

                                File.Copy(file, sTargetFileFullPath, true);
                                #endregion
                            }
                        }
                    }
                }
                #endregion

                #region 异常状态文件抽出 -> 迁移源备份目录[废弃]
                //bool IsRefreshFolder = false;
                //foreach (string item in strArray)
                //{
                //    if (string.IsNullOrWhiteSpace(item))
                //        continue;

                //    string sSubFilePath = item.Substring(6, item.Length - 6);
                //    sSourceFileFullPath = string.Format("{0}{1}", strSourcePath, sSubFilePath);
                //    sTargetFileFullPath = string.Format("{0}\\{1}{2}", strSourcePath, TMP_FOLDER_NAME, sSubFilePath);

                //    #region 如果存在上回操作残留备份目录，首先执行删除
                //    if (!IsRefreshFolder && Directory.Exists(string.Format("{0}\\{1}", strSourcePath, TMP_FOLDER_NAME)))
                //    {
                //        Cleaner.Clean(string.Format("{0}\\{1}", strSourcePath, TMP_FOLDER_NAME));
                //        IsRefreshFolder = true;
                //    }
                //    #endregion

                //    #region 备份文件
                //    string sSubDirectionPath = sTargetFileFullPath.Substring(0, sTargetFileFullPath.LastIndexOf("\\"));

                //    if (!Directory.Exists(sSubDirectionPath))
                //        Directory.CreateDirectory(sSubDirectionPath);

                //    File.Copy(sSourceFileFullPath, sTargetFileFullPath, true);
                //    #endregion
                //}
                #endregion
                bResult = true;
            }
            catch (Exception ex)
            {
                strMsg = "修正后文件拷贝失败！" + ex.Message;
            }
            return bResult;
        }

        /// <summary>
        /// 修正前文件拷贝(直拷贝)
        /// </summary>
        /// <param name="sFilePath"></param>
        /// <param name="strSourcePath"></param>
        /// <param name="sDirectionPath"></param>
        /// <returns></returns>
        public bool CopyLastVersionFile(List<string> sFilePath, string strSourcePath, string sDirectionPath, out string strMsg)
        {
            bool bResult = false;
            strMsg = "";
            string sTargetFileFullPath = string.Empty, sDireFolder = string.Empty;
            if (sFilePath.Count == 0 || string.IsNullOrWhiteSpace(strSourcePath) || string.IsNullOrWhiteSpace(sDirectionPath))
            {
                strMsg = "系统异常：无法定位待拷贝文件！";
                return false;
            }

            try
            {
                string[] strArryResult = sFilePath.ToArray();
                foreach (string item in strArryResult)
                {
                    if (string.IsNullOrWhiteSpace(item))
                        continue;

                    if (item.StartsWith("[ADD]"))
                        continue;

                    // 删除显示用提示文案 并 进行路径格式化
                    string sCurrentItem = item.Substring(7, item.Length - 7).Replace("\\", "/");

                    // 修正前文件目录作成
                    sTargetFileFullPath = string.Format("{0}\\0.修正前{1}", sDirectionPath, item.Substring(6, item.Length - 6));
                    sDireFolder = sTargetFileFullPath.Substring(0, sTargetFileFullPath.LastIndexOf("\\"));
                    if (!Directory.Exists(sDireFolder))
                        Directory.CreateDirectory(sDireFolder);

                    // 1. [命令作成]修正前文件迁移
                    string sCommandParam = makeCommand(strSourcePath, (int)CommandType.CMD_GET_SHOW_HEAD_TO_DIRECTION_FILE, sCurrentItem, sTargetFileFullPath);
                    // 2. [命令执行]修正前文件迁移
                    oTerminal.Exec(sCommandParam);
                }

                bResult = true;
            }
            catch (Exception ex)
            {
                strMsg = "修正后文件拷贝失败！" + ex.Message;
            }
            return bResult;
        }

        /// <summary>
        /// 还原修正前文件
        /// </summary>
        /// <param name="sFilePath"></param>
        /// <param name="strSourcePath"></param>
        /// <param name="sDirectionPath"></param>
        public bool RevertModifyFile(string sFilePath, string strSourcePath, out string strMsg)
        {
            bool bResult = false;
            strMsg = "";
            string sCommandParam, strFileInfo;

            try
            {
                sCommandParam = makeCommand(strSourcePath, (int)CommandType.CMD_GET_CHECKOUT_STASH);
                strFileInfo = oTerminal.Exec(sCommandParam);
                
                bResult = true;
            }
            catch (Exception ex)
            {
                strMsg = "还原修正文件失败！" + ex.Message;
            }

            return bResult;
        }

        /// <summary>
        /// 还原修正前文件并拷贝
        /// </summary>
        /// <param name="sFilePath"></param>
        /// <param name="strSourcePath"></param>
        /// <param name="sDirectionPath"></param>
        public bool RevertAndCopyFile(string sFilePath, string strSourcePath, string sDirectionPath, out string strMsg)
        {
            bool bResult = false;
            strMsg = "";
            string sCommandParam, strFileInfo, sSubFilePath, sSourceFileFullPath, sTargetFileFullPath;
            string[] strArray;

            if (string.IsNullOrWhiteSpace(sFilePath) || string.IsNullOrWhiteSpace(strSourcePath))
            {
                strMsg = "系统异常：无法定位待拷贝文件！";
                return false;
            }

            try
            {
                #region Revert File
                sCommandParam = makeCommand(strSourcePath, (int)CommandType.CMD_GET_CHECKOUT_STASH);
                strFileInfo = oTerminal.Exec(sCommandParam);
                #endregion

                #region 拷贝修正前文件
                strArray = sFilePath.Split(Environment.NewLine.ToCharArray());
                foreach (string item in strArray)
                {
                    if (string.IsNullOrWhiteSpace(item))
                        continue;

                    if (item.StartsWith("[ADD]"))
                        continue;

                    sSubFilePath = item.Substring(6, item.Length - 6);
                    sSourceFileFullPath = string.Format("{0}{1}", strSourcePath, sSubFilePath);
                    sTargetFileFullPath = string.Format("{0}\\0.修正前{1}", sDirectionPath, sSubFilePath);

                    #region 拷贝文件
                    string sSubDirectionPath = sTargetFileFullPath.Substring(0, sTargetFileFullPath.LastIndexOf("\\"));
                    if (!Directory.Exists(sSubDirectionPath))
                        Directory.CreateDirectory(sSubDirectionPath);

                    File.Copy(sSourceFileFullPath, sTargetFileFullPath, true);
                    #endregion
                }
                #endregion

                #region 迁移源异常备份文件还原
                foreach (string item in strArray)
                {
                    if (string.IsNullOrWhiteSpace(item))
                        continue;

                    sSubFilePath = item.Substring(6, item.Length - 6);
                    sSourceFileFullPath = string.Format("{0}\\{1}{2}", strSourcePath, TMP_FOLDER_NAME, sSubFilePath);
                    sTargetFileFullPath = string.Format("{0}{1}", strSourcePath, sSubFilePath);

                    string sSubDirectionPath = sTargetFileFullPath.Substring(0, sTargetFileFullPath.LastIndexOf("\\"));
                    if (!Directory.Exists(sSubDirectionPath))
                        Directory.CreateDirectory(sSubDirectionPath);

                    File.Copy(sSourceFileFullPath, sTargetFileFullPath, true);
                }

                // 删除临时文件夹
                //Directory.Delete(string.Format("{0}\\{1}", strSourcePath, TMP_FOLDER_NAME), true);
                Cleaner.Clean(string.Format("{0}\\{1}", strSourcePath, TMP_FOLDER_NAME));
                #endregion
                bResult = true;
            }
            catch (Exception ex)
            {
                strMsg = "修正前文件拷贝失败！" + ex.Message;
            }

            return bResult;
        }

        /// <summary>
        /// 遍历目录下所有文件
        /// </summary>
        /// <param name="sPath"></param>
        /// <returns></returns>
        public bool GetAllFolderFiles(string sPath, out List<string> lAddFile, out string strMsg)
        {
            bool bReturn = false;
            strMsg = "";
            lAddFile = new List<string>();
            try
            {
                DirectoryInfo dir = new DirectoryInfo(sPath);
                // 具体文件场合
                if (!string.IsNullOrWhiteSpace(dir.Extension))
                {
                    lAddFile.Add(dir.FullName);  //获取文件名
                    return true;
                }

                // 文件夹场合
                lAddFile = GetSubFolderFile(sPath);
            }
            catch (Exception ex)
            {
                strMsg = "系统异常" + ex.Message;
            }
            return bReturn;
        }

        private List<string> GetSubFolderFile(string sPath)
        {
            List<string> lAddFile = new List<string>();
            FileInfo[] files;
            try
            {
                DirectoryInfo dir = new DirectoryInfo(sPath);
                DirectoryInfo[] dirs = dir.GetDirectories();  //获取子目录
                files = dir.GetFiles("*.*");  //获取文件名
                foreach (DirectoryInfo d in dirs)
                {
                    lAddFile = GetSubFolderFile(dir + d.ToString() + "\\"); //递归调用
                }
                foreach (FileInfo f in files)
                {
                    lAddFile.Add(dir + f.ToString());
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return lAddFile;
        }
    }
}
